package com.rengwuxian.coursecoroutines._1_basics

import android.os.Bundle
import android.widget.TextView
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import com.rengwuxian.coursecoroutines.R
import com.rengwuxian.coursecoroutines.common.Contributor
import com.rengwuxian.coursecoroutines.common.gitHub
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.Disposable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

/**
 * 结构化并发初探 Structured concurrency
 * CoroutineScope、子协程、协程的取消、内存泄露、RxJava
 *
 * 挂起函数之所以关键，是因为它简化了并发任务的代码结构，用协程来写并发代码就比线程更好写，也更好读。 -- 面向的是并发任务的写法
 * 而结构化并发面向的是并发任务的管理。
 *
 * 协程的取消：避免资源不必要的浪费（内存泄露优化的一种）
 * 内存泄露：已经使用完的资源，不让GC（垃圾回收器）garbage collector回收它的内存。
 * gc原理：扫描内存里面的每个对象，看它有没有被几种类型的引用所指向。
 * 有哪几种引用呢？直接和间接引用都算。
 * 1.静态变量（static）
 * 2.活跃的线程（正在运行的）
 * 3.JNI （JVM无法判断，故都判断为有用）
 * 如果都没有被这几种引用，则gc会标记为可回收，下一次gc会直接回收。
 *
 * Activity是被系统所持有的，一旦系统不需要，则会解开引用。
 * 但是Activity中的线程最后有对应TextView的引用，
 * 那就是间接的引用了Activity，导致Activity不会被回收，
 * 直到线程执行完毕，变得不再活跃，则Activity会被释放，也就是几秒钟的资源消耗。
 *
 * RxJava：链式调用。subscribe时，会返回一个disposable，可以切断工作链条，在当前的下一步会放弃执行。
 * 第一时间就关闭，省内存，线程资源和cpu消耗等。
 * private fun rxStyle() = gitHub.contributorsRx("square", "retrofit")
 *     .map { list -> list.filter { it.contributions > 10 } }
 *     .observeOn(AndroidSchedulers.mainThread())
 *     .subscribe(::showContributors)
 *
 * 协程的写法
 * launch后返回一个Job对象，可以用这个来取消协程。job.cancel() 在onDestroy()中引用。
 * 默认lifecycleScope启动的协程，Jetpack提供的，已经有cancel的设计在里面，所以不用管这个。
 * 隐式Receiver，CoroutineScope
 *
 * 协程的取消，不仅会取消job所在的这个协程，会连同所有子协程、子子协程都取消掉。-- 结构化取消。
 * 协程是一层对多层的关系 -- 结构化并发的浅层概念
 *
 */
class StructuredConcurrencyActivity : ComponentActivity() {
  private lateinit var infoTextView: TextView
  private var diposable: Disposable? = null
  private var job: Job? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.layout_1)
    infoTextView = findViewById(R.id.infoTextView)

    // Structured concurrency
    // 内存泄露（泄漏） memory leak
    // GC（垃圾回收器） garbage collector
    // static
    // JNI
    // Android 内存泄露 弱引用
    // RxJava

//    diposable = rxStyle()
    job = coroutinesStyle()
    coroutinesStyle()
  }

  override fun onDestroy() {
    super.onDestroy()
//    diposable?.dispose()
//    job?.cancel()
    lifecycleScope.cancel()
  }

  private fun callbackStyle() {
    gitHub.contributorsCall("square", "retrofit")
      .enqueue(object : Callback<List<Contributor>> {
        override fun onResponse(
          call: Call<List<Contributor>>, response: Response<List<Contributor>>,
        ) {
          showContributors(response.body()!!)
          gitHub.contributorsCall("square", "okhttp")
            .enqueue(object : Callback<List<Contributor>> {
              override fun onResponse(
                call: Call<List<Contributor>>, response: Response<List<Contributor>>,
              ) {
                showContributors(response.body()!!)
              }

              override fun onFailure(call: Call<List<Contributor>>, t: Throwable) {
                t.printStackTrace()
              }
            })
        }

        override fun onFailure(call: Call<List<Contributor>>, t: Throwable) {
          t.printStackTrace()
        }
      })
  }

  private fun coroutinesStyle() = lifecycleScope.launch {
    launch {

    }
    val contributors = gitHub.contributors("square", "retrofit")
    val filtered = contributors.filter { it.contributions > 10 }
    showContributors(filtered)
  }

  private fun rxStyle() = gitHub.contributorsRx("square", "retrofit")
    .map { list -> list.filter { it.contributions > 10 } }
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(::showContributors)

  private fun showContributors(contributors: List<Contributor>) = contributors
    .map { "${it.login} (${it.contributions})" }
    .reduce { acc, s -> "$acc\n$s" }
    .let { infoTextView.text = it }
}